#pragma once

#include "Auxiliary.h"


/// Checks variation of network latency.
/// Will be used for determination of real perfomance of the dotsServer.
class PingPong
{
private:
	tbb::tbb_thread _thread;

	/// Starts server, which sends string "pong" for each connection.
	static inline void StartServer(size_t port)
	{
		boost::asio::io_service service;
		boost::asio::ip::tcp::acceptor acceptor(service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port));

		while (true)
		{
			try
			{
				boost::system::error_code error = boost::asio::error::host_not_found;
				// Accepting incoming connection.
				boost::asio::ip::tcp::socket socket(service);
				acceptor.accept(socket, error);
				if (error)
					throw std::runtime_error(error.message().c_str());
				// Receiving string "ping".
				char buf[5];
				socket.receive(boost::asio::buffer(buf, 5), 0, error);
				buf[4] = '\0';
				if (error || std::string(buf) != "ping")
					throw std::runtime_error("Cannot receive \"ping\"");
				// Sending answer "pong".
				socket.send(boost::asio::buffer(std::string("pong")), 0, error);
				if (error)
					throw std::runtime_error(error.message().c_str());
			}
			catch (std::exception &ex)
			{
				printf("PingPong error: %s\n", ex.what());
				tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.));
			}
		}
	}

	/// Starts client, which sends string "ping" to server and stores answer time.
	static inline void StartClient(ServerInfo &info)
	{
		boost::asio::io_service service;
		while (true)
		{
			try
			{
				boost::system::error_code error = boost::asio::error::host_not_found;
				// Connecting to server.
				boost::asio::ip::tcp::socket socket(service);
				socket.connect(boost::asio::ip::tcp::endpoint(info.GetIpAddress(), 666), error);

				if (error)
					throw std::runtime_error(error.message().c_str());
				// Sending "ping".
				socket.send(boost::asio::buffer(std::string("ping")), 0, error);
				if (error)
					throw std::runtime_error(error.message().c_str());
				// Receiving "pong".
				char buf[5];
				socket.receive(boost::asio::buffer(buf, 5), 0, error);
				buf[4] = '\0';
				if (error || std::string(buf) != "pong")
					throw std::runtime_error("Cannot receive \"pong\"");
				// Finishing and registering request.
				socket.close();
				Stats::GetRef()->RegisterPingPongRequest();
			}
			catch (std::exception &ex)
			{
				printf("PingPong error: %s\n", ex.what());
				tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.));
			}
            tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1.));
		}
	}

public:
	/// Starts PingPong server in new thread (using 666 port) and [optionally] blocks current.
	inline PingPong(bool shouldBlock)
		:_thread(StartServer, 666)
	{
		// Sleeping
		if (shouldBlock)
			while (true)
				tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1000000.0f));
	}

	/// Starts PingPong client in new thread and [optionally] blocks current.
	inline PingPong(ServerInfo &info, bool shouldBlock)
		:_thread(StartClient, info)
	{
		// Sleeping
		if (shouldBlock)
			while (true)
				tbb::this_tbb_thread::sleep(tbb::tick_count::interval_t(1000000.0f));
	}
};
